# app/service/banpick_service.py    主要为无返回值的接收请求设置状态
from utils.token_util import get_user_id_by_token
from db.redis_part import xibie_redis
from db.mysql_part import get_mysql_session
from db.models import Pet, PetType
from app.config import (
    REDIS_GAME_PREFIX, REDIS_GAME_EXPIRE
)
from app.service.game_service import get_game_id, get_game_side, get_curr_phase_info, generate_phase, save_game_log
from app.service.group_service import get_group_config, get_group_pet_bans
from app.handler.ws_manage import ws_manager, generate_event
from app.handler.delay_manage import delay_manager
from app.exception.cus_exception import InvalidActionException
import json
import random
from sqlalchemy import select
from utils.log_util import logger


async def roll_pet_type(game_id, phase):
    """随机抽取属系"""
    pet_pick_count = phase['count'] # 当前是该类型下的第几次pick
    side = phase['side'] # 当前是哪个玩家pick
    async with get_mysql_session() as session:
        # 获取所有基础单属系的id
        stmt = select(PetType).where(PetType.sub_type1.is_(None), PetType.sub_type2.is_(None))
        res = await session.execute(stmt)
        base_pet_types = list(res.scalars().all())
        base_pet_types = [pet_type.pet_type_id for pet_type in base_pet_types] 
        # 先随机抽取一个属系的下标
        index = random.randint(0, len(base_pet_types) - 1) # 
        
        # 判断规则
        if xibie_redis.hexists(f"{REDIS_GAME_PREFIX}{game_id}", f"uniqueType"):
            # 不会出现重复属系（规则3）
            player1_picks = xibie_redis.lrange(f"{REDIS_GAME_PREFIX}{game_id}-player1-pet-picked", 0, -1)
            player2_picks = xibie_redis.lrange(f"{REDIS_GAME_PREFIX}{game_id}-player2-pet-picked", 0, -1)
            all_picks = player1_picks + player2_picks
            all_picks_type = [json.loads(pick)['pet_type'] for pick in all_picks]
            # 将已经出现的属系（sp除外）从可抽取属系中移除
            types_to_remove = []
            for pet_type_id in base_pet_types:
                if pet_type_id in all_picks_type and pet_type_id != 8:
                    types_to_remove.append(pet_type_id)
            for pet_type_id in types_to_remove:
                base_pet_types.remove(pet_type_id)
            logger.info(f"当前规则3可抽取属系：{base_pet_types}")
            index = random.randint(0, len(base_pet_types) - 1)
        if xibie_redis.hexists(f"{REDIS_GAME_PREFIX}{game_id}", f'player1TypeBanned') or xibie_redis.hexists(
                f"{REDIS_GAME_PREFIX}{game_id}", f'player2TypeBanned'):
            # 有禁用属系（规则4）
            player1_type_ban = int(xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", f'player1TypeBanned')) if xibie_redis.hexists(f"{REDIS_GAME_PREFIX}{game_id}", f'player1TypeBanned') else 0
            player2_type_ban = int(xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", f'player2TypeBanned')) if xibie_redis.hexists(f"{REDIS_GAME_PREFIX}{game_id}", f'player2TypeBanned') else 0

            base_pet_types.remove(player1_type_ban)
            base_pet_types.remove(player2_type_ban)
            index = random.randint(0, len(base_pet_types) - 1)


        # 判断系统随机还是玩家手动
        if not xibie_redis.exists(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked") \
                or xibie_redis.llen(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked") < pet_pick_count:
            # 没有新pick对象，说明是系统初始化随机属系
            xibie_redis.rpush(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", json.dumps({
                "pet_type": base_pet_types[index],
            }))
            xibie_redis.expire(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", REDIS_GAME_EXPIRE)
        else:
            # 已有pick对象，说明是玩家重抽随机属系
            rule_first = xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", "ruleFirst")
            rule_second = xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", "ruleSecond")
            if rule_first == 'rule5' or rule_second == 'rule5':
                # 合并次数
                if int(xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}BothCount')) <= 0:
                    raise InvalidActionException("重抽次数用完", game_id)
            else:
                if int(xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}RollCount')) <= 0:
                    raise InvalidActionException("重抽次数用完", game_id)
            pick = json.loads(xibie_redis.lindex(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", pet_pick_count - 1))
            if 'pet_type' in pick:
                logger.info(pick['pet_type'])
                stmt_2 = select(PetType).where(PetType.sub_type1.is_(None), PetType.sub_type2.is_(None), PetType.pet_type_id != pick['pet_type'])
                res_2 = await session.execute(stmt_2)
                base_pet_types = list(res_2.scalars().all())
                base_pet_types = [pet_type.pet_type_id for pet_type in base_pet_types]
                index = random.randint(0, len(base_pet_types) - 1)
            pick['pet_type'] = base_pet_types[index]
            xibie_redis.lset(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", pet_pick_count - 1, json.dumps(pick))
            # 重抽次数减一
            if xibie_redis.hexists(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}RollCount') and int(xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}RollCount')) > 0:
                xibie_redis.hincrby(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}RollCount', -1)
            if xibie_redis.hexists(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}BothCount') and int(xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}BothCount')) > 0:
                # 规则五下，和暗手共用次数
                xibie_redis.hincrby(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}BothCount', -1)

async def to_next_phase(game_id):
    """切换到下一个phase

    Args:
        game_id (_type_): _description_
    """
    # 弹出当前阶段
    xibie_redis.lpop(f"{REDIS_GAME_PREFIX}{game_id}-phase")

    # 获取下一个阶段
    next_phase = xibie_redis.lindex(f"{REDIS_GAME_PREFIX}{game_id}-phase", 0)
    logger.info(f"{game_id} 下一阶段 {next_phase}")
    if next_phase is None:
        # 已经没有下一个阶段了，bp结束
        await ws_manager.broadcast(game_id, generate_event('banpickOver', {}))
        await delay_manager.cancel_timer(game_id)
        logger.info(f"{game_id} bp结束")
        await save_game_log(game_id)
        return
    next_phase = json.loads(next_phase)
    if next_phase["operation"] == "pick":
        if next_phase['item'] == "pet":
            pet_pick_count = next_phase['count'] # 当前是该类型下的第几次pick
            side = next_phase['side'] # 当前是哪个玩家pick
            if pet_pick_count == 3 \
                and xibie_redis.exists(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked") \
                and xibie_redis.hexists(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}TypeThird'):
                # 到达第三手pick时，判断是否有pick属系（规则1），有就跳过自动重抽
                pass
            else:
                # 如果是pick精灵，需要抽取属系
                await roll_pet_type(game_id, next_phase)
                logger.debug(f"房间：{game_id}，随机属系")
            
    count_time = int(xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", "countTime"))
    await delay_manager.start_timer(game_id, count_time + 2, next_phase, ban_pick_time_out)
    await ws_manager.broadcast(game_id, generate_event('nextPhase', {}))


async def ban_pick_time_out(game_id):
    """bp超时处理

    Args:
        game_id (_type_): _description_
    """
    async with delay_manager.get_game_lock(game_id):
        curr_phase = get_curr_phase_info(game_id)
        await to_next_phase(game_id)
        phase_count = curr_phase['count']
        side = curr_phase['side']
        if curr_phase['operation'] == "pick":
            if curr_phase['item'] == "pet":
                # 精灵空pick
                curr_pick = json.loads(
                    xibie_redis.lindex(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", phase_count - 1))
                curr_pick['pet_id'] = 0
                curr_pick['hidden'] = False
                curr_pick['pet_origin_id'] = 0
                curr_pick['pet_head'] = '1.png'
                xibie_redis.lset(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", phase_count - 1, json.dumps(curr_pick))

    


async def player_roll_pet_type(token):
    """玩家主动随机精灵（消耗次数）

    Args:
        token (_type_): _description_

    Raises:
        InvalidActionException: _description_
    """
    game_id = get_game_id(token)
    user_id = get_user_id_by_token(token)
    game_side = get_game_side(game_id, user_id)
    curr_phase = get_curr_phase_info(game_id)
    count_time = int(xibie_redis.hget(f"{REDIS_GAME_PREFIX}{game_id}", "countTime"))
    if game_side != curr_phase['side']:
        raise InvalidActionException("不是当前玩家操作", user_id, game_id)
    await roll_pet_type(game_id, curr_phase)
    async with delay_manager.get_game_lock(game_id):
        await delay_manager.restart_timer(game_id, count_time + 2, ban_pick_time_out)
    await ws_manager.broadcast(game_id, generate_event('typeRoll', {}))
    logger.debug(f"房间：{game_id}，玩家{user_id}主动重随属系")


async def player_press_button(token: str, payload):
    """玩家点击操作按钮

    Args:
        token (str): _description_
        payload (_type_): _description_
    """
    user_id = get_user_id_by_token(token)
    game_id = get_game_id(token)
    game_side = get_game_side(game_id, user_id)
    game_info = xibie_redis.hgetall(f'{REDIS_GAME_PREFIX}{game_id}')
    logger.debug(f"房间{game_id}，玩家{user_id}，点击了按钮操作：{payload}")
    if 'wait' in game_info:
        await ready_for_start(user_id, game_id, game_info)
    else:
        phase = get_curr_phase_info(game_id)
        if 'operation' in phase:
            if phase['operation'] == 'pick' or phase['operation'] == 'ban':
                logger.info(f"房间{game_id}，玩家{user_id}，进行了banpick操作：{payload}")
                async with delay_manager.get_game_lock(game_id):
                    await player_ban_pick(user_id, game_id, game_info, phase, payload)
                    await to_next_phase(game_id)
                


async def ready_for_start(user_id, game_id, game_info):
    """玩家准备开始

    Args:
        user_id (_type_): _description_
        game_id (_type_): _description_
        game_info (_type_): _description_
    """
    if game_info['player1'] == user_id:
        xibie_redis.hset(f'{REDIS_GAME_PREFIX}{game_id}', f'player1Ready', '1')
        await ws_manager.broadcast(game_id, generate_event('ready', {'side': 'player1'}))
        logger.info(f"对局{game_id}，蓝方已准备")
    elif game_info['player2'] == user_id:
        xibie_redis.hset(f'{REDIS_GAME_PREFIX}{game_id}', f'player2Ready', '1')
        await ws_manager.broadcast(game_id, generate_event('ready', {'side': 'player2'}))
        logger.info(f"对局{game_id}，红方已准备")

    game_info = xibie_redis.hgetall(f'{REDIS_GAME_PREFIX}{game_id}')
    if 'player1Ready' in game_info and game_info['player1Ready'] == '1' \
            and 'player2Ready' in game_info and game_info['player2Ready'] == '1':
        await ws_manager.broadcast(game_id, generate_event('allReady', {}))

        generate_phase(game_id)
        xibie_redis.hdel(f'{REDIS_GAME_PREFIX}{game_id}', 'wait')
        xibie_redis.hdel(f'{REDIS_GAME_PREFIX}{game_id}', 'player1Ready')
        xibie_redis.hdel(f'{REDIS_GAME_PREFIX}{game_id}', 'player2Ready')

        await delay_manager.start_timer(game_id, 12, {
            "operation": "readyStart",
            "item": "game",
            "side": "all",
            "count": 1
        }, to_next_phase)
        logger.info(f"对局{game_id}，双方都已准备")


async def player_ban_pick(user_id, game_id, game_info, curr_phase, payload):
    """玩家进行禁用或选择操作

    Args:
        user_id (_type_): _description_
        game_id (_type_): _description_
        game_info (_type_): _description_
        curr_phase (_type_): _description_
        payload (_type_): _description_
    """
    side = get_game_side(game_id, user_id)
    if side != curr_phase['side']:
        raise InvalidActionException("不是当前玩家操作", user_id, game_id)
    rival_side = 'player1' if side == 'player2' else 'player2' if side == 'player1' else None

    group_id = int(game_info['groupId'])
    group_config = await get_group_config(group_id)
    group_bans = await get_group_pet_bans(group_id)
    group_unhide = [4643]  # 暂定蓓丽安特一位
    group_unhide_types = [8]  # 暂定只有sp属系

    pet_picked = xibie_redis.lrange(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", 0, -1)
    pet_picked_ids = [] # 己方已pick精灵
    pet_picked_origin_ids = [] # 己方已pick精灵的原始id
    for pick in pet_picked:
        temp = json.loads(pick)
        if 'pet_id' in temp:
            pet_picked_ids.append(temp['pet_id'])
            pet_picked_origin_ids.append(temp['pet_origin_id'])
    suit_picked = game_info[f'{side}SuitPicked'] if f'{side}SuitPicked' in game_info else None
    phase_count = curr_phase['count']
    if curr_phase['operation'] == "pick":
        if curr_phase['item'] == "pet":
            payload_origin_id = payload['pet_origin_id']
            curr_pick = json.loads(
                xibie_redis.lindex(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", phase_count - 1))
            # 检查是否公ban
            if group_bans.__contains__(payload['value']):
                raise InvalidActionException("该精灵已被ban", user_id, game_id)
            
            # 检查是否重复，需要根据pet_origin_id检查
            if 'pet_origin_id' not in payload:
                raise InvalidActionException("未选择", user_id, game_id)
            
            if pet_picked_origin_ids.__contains__(payload_origin_id):
                raise InvalidActionException(f"不能选择重复的精灵{payload_origin_id}", user_id, game_id)
            rival_pet_picked = xibie_redis.lrange(f"{REDIS_GAME_PREFIX}{game_id}-{rival_side}-pet-picked", 0, -1)
            for pick in rival_pet_picked:
                pet = json.loads(pick)
                if 'pet_origin_id' in pet:
                    if pet['pet_origin_id'] == payload_origin_id and not pet['hidden']: # 跳过暗手
                        raise InvalidActionException("该精灵已被pick", user_id, game_id)
            
            # 判断暗手
            if payload['hidden']:
                # 检查是否暗手次数用完
                if f'{side}BothCount' in game_info and int(game_info[f'{side}BothCount']) <= 0:
                    raise InvalidActionException("暗手次数用完", user_id, game_id)
                if f'{side}HideCount' in game_info and int(game_info[f'{side}HideCount']) <= 0:
                    raise InvalidActionException("暗手次数用完", user_id, game_id)

                if group_unhide.__contains__(payload['value']) or group_unhide.__contains__(payload_origin_id):
                    raise InvalidActionException("该精灵不能暗手", user_id, game_id)
                if group_unhide_types.__contains__(curr_pick['pet_type']):
                    raise InvalidActionException("该属系不能暗手", user_id, game_id)
            # 判断限制池
            for limit_config in group_config['limit_config']:
                limit_num = int(limit_config['limit_num'])
                limit_count = 0

                pet_picked_ids.append(payload['value'])
                for pool in limit_config['limit_pool']:
                    if pool['item'] == 'pet':
                        # 检查已经pick的精灵有没有在限制里的
                        if pool['trigger'] == 'or':
                            # or表示有一只就算
                            for pet_id in pool['values']:
                                if pet_id in pet_picked_ids:
                                    limit_count += 1
                                    break
                        elif pool['trigger'] == 'and':
                            # and表示必须全有，即pool['values']必须是pet_picked_ids的子集
                            if set(pool['values']).issubset(pet_picked_ids):
                                limit_count += 1

                    if pool['item'] == 'suit' and suit_picked:
                        # 检查已经pick的套装有没有在限制里的
                        if pool['trigger'] == 'or':
                            # or表示有一个就算，套装只能选一个，不存在and判断
                            if suit_picked in pool['values']:
                                limit_count += 1
                                break
                if limit_count > limit_num:
                    raise InvalidActionException("超出限制", user_id, game_id)

            async with get_mysql_session() as session:
                stmt = select(PetType).join(Pet, PetType.pet_type_id == Pet.pet_type).where(
                        Pet.pet_id == payload['value'])
                res = await session.execute(stmt)
                pet_type = res.scalars().first()
                
                # 判断属系对不对
                if pet_type.pet_type_id != curr_pick['pet_type'] \
                    and pet_type.sub_type1 != curr_pick['pet_type'] \
                        and pet_type.sub_type2 != curr_pick['pet_type']:
                    raise InvalidActionException("属系错误", user_id, game_id)
                
                # 判断老六
                if 'onlyDerive' in game_info:
                    if pet_type.pet_type_id != 8 and pet_type.sub_type1 is None:
                        raise InvalidActionException("只能选择衍生的双属性精灵", user_id, game_id)
                    
                # 如果暗手，扣除次数
                if payload['hidden']:
                    if f'{side}BothCount' in game_info:
                        xibie_redis.hset(f'{REDIS_GAME_PREFIX}{game_id}', f'{side}BothCount', int(game_info[f'{side}BothCount']) - 1)
                    if f'{side}HideCount' in game_info:
                        xibie_redis.hset(f'{REDIS_GAME_PREFIX}{game_id}', f'{side}HideCount', int(game_info[f'{side}HideCount']) - 1)
                

                curr_pet = await session.get(Pet, payload['value'])
                curr_pick['pet_id'] = payload['value']
                curr_pick['pet_origin_id'] = curr_pet.pet_origin_id
                curr_pick['pet_name'] = curr_pet.pet_name
                curr_pick['pet_head'] = curr_pet.head_hs if curr_pet.head_hs else f"{curr_pet.pet_origin_id}.png"
                curr_pick['hidden'] = payload['hidden']
                curr_pick['ex_mark'] = curr_pet.ex_mark
                xibie_redis.lset(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", phase_count - 1, json.dumps(curr_pick))
            
            
        elif curr_phase['item'] == "suit":
            # 判断套装是否被禁用，系别战中禁用的套装只是对方不能用
            if f'{rival_side}SuitBanned' in game_info and int(game_info[f'{rival_side}SuitBanned']) == payload['value']:
                raise InvalidActionException("对方已禁用该套装", user_id, game_id)
            
            # TODO # 判断限制池
            for limit_config in group_config['limit_config']:
                limit_num = int(limit_config['limit_num'])
                limit_count = 0

                for pool in limit_config['limit_pool']:
                    if pool['item'] == 'pet':
                        # 检查已经pick的精灵有没有在限制里的
                        if pool['trigger'] == 'or':
                            # or表示有一只就算
                            for pet_id in pool['values']:
                                if pet_id in pet_picked_ids:
                                    limit_count += 1
                                    break
                        elif pool['trigger'] == 'and':
                            # and表示必须全有，即pool['values']必须是pet_picked_ids的子集
                            if set(pool['values']).issubset(pet_picked_ids):
                                limit_count += 1

                    if pool['item'] == 'suit':
                        # 检查要pick的套装有没有在限制里的
                        # 套装只能选一个，不存在and判断
                        if payload['value'] in pool['values']:
                            limit_count += 1
                            break
                if limit_count > limit_num:
                    raise InvalidActionException("超出限制", user_id, game_id)
            
            xibie_redis.hset(f"{REDIS_GAME_PREFIX}{game_id}", f"{side}SuitPicked", payload['value'])
            
            
        elif curr_phase['item'] == "pet_type":
            # 判断属系是否被禁用
            if f'{rival_side}TypeBanned' in game_info and int(game_info[f'{rival_side}TypeBanned']) == payload['value']:
                raise InvalidActionException("已禁用该属系", user_id, game_id)
            elif f'{side}TypeBanned' in game_info and int(game_info[f'{side}TypeBanned']) == payload['value']:
                raise InvalidActionException("已禁用该属系", user_id, game_id)
            # 指定第三只的属系

            curr_pick = {'pet_type': payload['value']}
            xibie_redis.rpush(f"{REDIS_GAME_PREFIX}{game_id}-{side}-pet-picked", json.dumps(curr_pick))
            xibie_redis.hset(f"{REDIS_GAME_PREFIX}{game_id}", f'{side}TypeThird', payload['value'])
                
    if curr_phase['operation'] == 'ban':
        if curr_phase['item'] == "suit":
            xibie_redis.hset(f"{REDIS_GAME_PREFIX}{game_id}", f"{side}SuitBanned", payload['value'])
        elif curr_phase['item'] == "pet_type":
            if payload['value'] == 8:
                raise InvalidActionException("sp属系不能禁用", user_id, game_id)
            xibie_redis.hset(f"{REDIS_GAME_PREFIX}{game_id}", f"{side}TypeBanned", payload['value'])
    
